/*@@

	Copyright (c) 2010 BlueSky LTD. All rights reserved. 

	Created_datetime : 	2010-6-22

	File Name :	base_xpackfile.cpp

	Author : liusiliang (siliangliu@msn.com)

	Description : 

@@*/


#include <cstdlib>

#include "basetype.h"
#include "zlib/zlib.h"
#include "base_xpackfile.h"
#include "base/base_packmgr.h"

namespace BLK
{
	//////////////////////////////////////////////////////////////////////////
	//						Class Definition
	//////////////////////////////////////////////////////////////////////////

	XPackFile::XPackElemFileCache	XPackFile::ms_ElemFileCache[MAX_XPACKFILE_CACHE];
	int								XPackFile::ms_nNumElemFileCache = 0;
	bool							XPackFile::ms_bInitialize = false;
#ifdef WIN32
	CRITICAL_SECTION				XPackFile::ms_ReadCritical;
#else
	void* _aligned_malloc(uint sz, uint a)
	{
		void* p;
		posix_memalign(&p, a, sz);
		return p;
	}
#define _aligned_realloc(p,sz,a) realloc(p,sz)
#define _aligned_free free
#endif

	/*
	 *	功能：初始化包文件模块
	 *	返回：是否初始化成功
	 */
	bool XPackFile::Initialize(void)
	{
 		if(ms_bInitialize == false)
		{
#ifdef WIN32
			::InitializeCriticalSection(&ms_ReadCritical);
#endif
 			ms_bInitialize = true;
 		}
 
 		return ms_bInitialize;
	}

	/*
	 *	功能：关闭包文件模块
	 */
	void XPackFile::Terminate(void)
	{
		if(ms_bInitialize)
		{
			for(int i = 0; i < ms_nNumElemFileCache; i++)
				FreeElemCache(i);

			ms_nNumElemFileCache = 0;

#ifdef WIN32
			::DeleteCriticalSection(&ms_ReadCritical);
#endif
			ms_bInitialize = false;
		}
	}

	/*
	 *	功能：构造函数
	 */
	XPackFile::XPackFile(void)
	{
		m_uFileSize = 0;
		m_pIndexList = NULL;
		m_nElemFileCount = 0;
	}

	/*
	 *	功能：析构函数
	 */
	XPackFile::~XPackFile(void)
	{
		Close();
	}

	/*
	 *	功能：打开包文件
	 *	参数：	pszPackFileName --> 打开文件的路径
	 *			nSelfIndex --> 	包自己的索引
	 *	返回：是否正确打开
	 */
	bool XPackFile::Open(
		const char* pszPackFileName, 
		int nSelfIndex)
	{
		if(ms_bInitialize == false)
			return false;

		bool bResult = false;
		Close();

#ifdef WIN32
		::EnterCriticalSection(&ms_ReadCritical);
#endif
		
		m_strFileName = pszPackFileName;
		m_nSelfIndex = nSelfIndex;

		m_File.open(pszPackFileName);
		while(m_File.good())
		{
			unsigned long dwFileSize;
			m_File.seekg(0, std::ios::end);
			dwFileSize = m_File.tellg();
			m_File.seekg(0, std::ios::beg);

			m_uFileSize = (unsigned)dwFileSize;
						
			if(m_uFileSize == 0 || 
				m_uFileSize == INFINITE_ ||
				m_uFileSize <= sizeof(PackFileHeader))
			{
				break;
			}

			PackFileHeader		Header;
			unsigned long		dwListSize;
			unsigned			uReaded;

			//--读取包文件头--
			m_File.read((char*)&Header, sizeof(Header));
			uReaded = m_File.gcount();

			//--包文件标记与内容的合法性判断--
			if(uReaded != sizeof(Header) ||
				*(int*)(&Header.cSignature) != XPACKFILE_SIGNATURE_FLAG ||
				Header.uCount == 0 ||
				Header.uIndexTableOffset < sizeof(PackFileHeader) ||
				Header.uIndexTableOffset >= m_uFileSize ||
				Header.uDataOffset < sizeof(PackFileHeader) ||
				Header.uDataOffset >= m_uFileSize)
			{
				break;
			}

			//--读取索引信息表--
			dwListSize = sizeof(PackIndexInfo) * Header.uCount;
			m_pIndexList = (PackIndexInfo*)_aligned_malloc(
				dwListSize, 
				MEM_ALIGNED_ALLOC_SIZE_16);

			unsigned long dwIndexTableOffset;
			m_File.seekg(Header.uIndexTableOffset, std::ios::beg);
			dwIndexTableOffset = m_File.tellg();

			if(m_pIndexList == NULL ||
				dwIndexTableOffset != Header.uIndexTableOffset)
			{
				break;
			}

			m_File.read((char*)m_pIndexList, dwListSize);
			uReaded = m_File.gcount();

			if(uReaded != dwListSize)
				break;

			m_nElemFileCount = Header.uCount;
			bResult = true;

			break;
		};

		if(bResult == false)
			Close();

#ifdef WIN32
		::LeaveCriticalSection(&ms_ReadCritical);
#endif

		return bResult;
	}

	/*
	 *	功能：关闭包文件
	 */
	void XPackFile::Close(void)
	{
		if(ms_bInitialize == false)
			return;

#ifdef WIN32
		::EnterCriticalSection(&ms_ReadCritical);
#endif
		if(m_pIndexList)
		{
			//----清除cache中缓存到的（可能）是此包中的子文件----
			for(int i = ms_nNumElemFileCache - 1; i >= 0; i--)
			{
				if(ms_ElemFileCache[i].nPackIndex == m_nSelfIndex)
				{
					FreeElemCache(i);
					ms_nNumElemFileCache--;

					for(int j = i; j < ms_nNumElemFileCache; j++)
						ms_ElemFileCache[j] = ms_ElemFileCache[j + 1];
				}
			}
			::_aligned_free(m_pIndexList);
			m_pIndexList = NULL;
		}
		m_nElemFileCount = 0;

		m_File.close();
		m_uFileSize = 0;

#ifdef WIN32
		::LeaveCriticalSection(&ms_ReadCritical);
#endif
	}

	/*
	 *	功能：释放一个cache结点的数据
	 *	参数：nCacheIndex	节点在cache中的索引
	 */
	void XPackFile::FreeElemCache(int nCacheIndex)
	{
		if(nCacheIndex < 0 || 
			nCacheIndex >= ms_nNumElemFileCache)
			return;

		if(ms_ElemFileCache[nCacheIndex].pBuffer)
		{
			::_aligned_free(ms_ElemFileCache[nCacheIndex].pBuffer);
			ms_ElemFileCache[nCacheIndex].pBuffer = NULL;
		}

		ms_ElemFileCache[nCacheIndex].uId = 0;
		ms_ElemFileCache[nCacheIndex].lSize = 0;
		ms_ElemFileCache[nCacheIndex].uRefFlag = 0;
		ms_ElemFileCache[nCacheIndex].nPackIndex = -1;
	}

	/*
	 *	功能：直接读取包文件数据中的数据到缓冲区
	 *	参数：	pBuffer --> 用来读取数据的缓冲区
	 *			uOffset --> 文件在包中的偏移
	 *			uSize --> 需要读取的大小
	 *	返回：成功与否
	 */
	bool XPackFile::DirectRead(
		void* pBuffer, 
		unsigned uOffset, 
		unsigned uSize)
	{
		bool bResult = false;
		unsigned uReaded;

		if(!pBuffer ||
			!m_File.good())
		{
			return false;
		}

		unsigned long ulReadOffset;
		m_File.seekg(uOffset, std::ios::beg);
		ulReadOffset = m_File.tellg();

		if(uOffset + uSize <= m_uFileSize &&
			ulReadOffset == uOffset)
		{
			m_File.read((char*)pBuffer, uSize);
			uReaded = m_File.tellg();

			if(uReaded == uSize)
				bResult = true;
		}

		return bResult;
	}

	/*
	 *	功能：带解压地读取包文件到缓冲区
	 *	参数：	pBuffer --> 缓冲区指针
	 *			uExtractSize  --> 数据（期望）解压后的大小，pBuffer缓冲区的大小不小于此数
	 *			lCompressType --> 直接从包中度取得原始（/压缩）大小
	 *			uOffset  --> 从包中的此偏移位置开始读取
	 *			uSize    --> 从包中直接读取得（压缩）数据的大小
	 *	返回：成功与否
	 */
	bool XPackFile::ExtractRead(
		void* pBuffer, 
		unsigned uExtractSize,
		long lCompressType, 
		unsigned uOffset, 
		unsigned uSize)
	{
		if(!pBuffer)
			return false;

		bool bResult = false;
		if(lCompressType == TYPE_NONE)
		{
			if(uExtractSize == uSize)
				bResult = DirectRead(pBuffer, uOffset, uSize);
		}
		else
		{
			void* pReadBuffer = _aligned_malloc(
				uSize, 
				MEM_ALIGNED_ALLOC_SIZE_16);
			
			if(pReadBuffer)
			{
				if(lCompressType == TYPE_ZLIB && 
					DirectRead(pReadBuffer, uOffset, uSize))
				{
					unsigned long uDestLength;
					if(Z_OK != uncompress(
						(BYTE*)pBuffer, 
						&uDestLength,
						(BYTE*)pReadBuffer, 
						uSize))
					{
						::_aligned_free(pReadBuffer);
						return false;
					}
					
					bResult =  (uDestLength == uExtractSize);
				}
				::_aligned_free(pReadBuffer);
			}
		}

		return bResult;
	}

	/*
	 *	功能：在索引表中查找子文件项(二分法找)
	 *	参数：	ulId --> 需要查找的文件名ID
	 *	返回：如找到返回在索引表中的位置(>=0)，如未找到返回-1
	 */
	int XPackFile::FindElemFile(unsigned long ulId) const
	{
		int nBegin, nEnd, nMid;
		nBegin = 0;
		nEnd = m_nElemFileCount - 1;

		while(nBegin <= nEnd)
		{
			nMid = (nBegin + nEnd) / 2;
			if(ulId < m_pIndexList[nMid].uId)
				nEnd = nMid - 1;
			else if(ulId > m_pIndexList[nMid].uId)
				nBegin = nMid + 1;
			else
				break;
		}
		return ((nBegin <= nEnd) ? nMid : -1);
	}

	/*
	 *	功能：查找包内的子文件
	 *	参数：	uId --> 子文件的id
	 *			ElemRef -->如果找到则在此结构里填上子文件的相关信息
	 *	返回：是否找到
	 */
	bool XPackFile::FindElemFile(
		unsigned long uId, 
		PackElemFileRef& ElemRef)
	{
		if(ms_bInitialize == false)
			return false;

		ElemRef.nElemIndex = -1;
		if(uId)
		{
#ifdef WIN32
			::EnterCriticalSection(&ms_ReadCritical);
#endif

			ElemRef.nCacheIndex = FindElemFileInCache(uId, -1);
			if(ElemRef.nCacheIndex >= 0)
			{
				ElemRef.uId = uId;
				ElemRef.nPackIndex = ms_ElemFileCache[ElemRef.nCacheIndex].nPackIndex;
				ElemRef.nElemIndex = ms_ElemFileCache[ElemRef.nCacheIndex].nElemIndex;
				ElemRef.nSize = ms_ElemFileCache[ElemRef.nCacheIndex].lSize;
				ElemRef.nOffset = 0;
			}
			else
			{
				ElemRef.nElemIndex = FindElemFile(uId);
				if(ElemRef.nElemIndex >= 0)
				{
					ElemRef.uId = uId;
					ElemRef.nPackIndex = m_nSelfIndex;
					ElemRef.nOffset = 0;
					ElemRef.nSize = m_pIndexList[ElemRef.nElemIndex].lSize;
				}
			}

#ifdef WIN32
			::LeaveCriticalSection(&ms_ReadCritical);
#endif
		}

		return (ElemRef.nElemIndex >= 0);
	}

	/*
	 *	功能：分配缓冲区，并读包内的子文件的内容到其中
	 *	参数：子文件在包内的索引
	 *	返回：成功则返回缓冲区的指针，否则返回空指针
	 */
	void* XPackFile::ReadElemFile(int nElemIndex)
	{
		if(nElemIndex < 0 || 
			nElemIndex >= m_nElemFileCount)
			return NULL;

		void* pDataBuffer = _aligned_malloc(
			m_pIndexList[nElemIndex].lSize, 
			MEM_ALIGNED_ALLOC_SIZE_16);

		if(pDataBuffer)
		{
			if(ExtractRead(pDataBuffer,
					m_pIndexList[nElemIndex].lSize,
					(m_pIndexList[nElemIndex].lCompressSizeFlag & TYPE_FILTER),
					m_pIndexList[nElemIndex].uOffset,
					(m_pIndexList[nElemIndex].lCompressSizeFlag & (~TYPE_FILTER))) == false)
			{
				::_aligned_free(pDataBuffer);
				pDataBuffer = NULL;
			}
		}
		return pDataBuffer;
	}

	/*
	 *	功能：在cache里查找子文件
	 *	参数：	uId --> 子文件id
	 *			nDesireIndex --> 在cache中的可能位置
	 *	返回：成功则返回cache节点索引(>=0),失败则返回-1
	 */
	int XPackFile::FindElemFileInCache(
		unsigned uId,
		int nDesireIndex)
	{
		if(nDesireIndex >= 0 && 
			nDesireIndex < ms_nNumElemFileCache &&
			uId == ms_ElemFileCache[nDesireIndex].uId)
		{
			ms_ElemFileCache[nDesireIndex].uRefFlag = INFINITE_;
			return nDesireIndex;
		}

		nDesireIndex = -1;
		for(int i = 0; i < ms_nNumElemFileCache; i++)
		{
			if(uId == ms_ElemFileCache[i].uId)
			{
				ms_ElemFileCache[i].uRefFlag = INFINITE_;
				nDesireIndex = i;
				break;
			}
		}
		return nDesireIndex;
	}

	/*
	 *	功能：把子文件数据添加到cache
	 *	参数：	pBuffer --> 存有子文件数据的缓冲区
	 *			nElemIndex --> 子文件在包中的索引
	 *	返回：添加到cache的索引位置
	 */
	int XPackFile::AddElemFileToCache(
		void* pBuffer, 
		int nElemIndex)
	{
		if(!pBuffer ||
			nElemIndex < 0 || 
			nElemIndex >= m_nElemFileCount)
			return -1;

		int nCacheIndex;
		if(ms_nNumElemFileCache < MAX_XPACKFILE_CACHE)
		{	
			//找到一个空位置
			nCacheIndex = ms_nNumElemFileCache++;
		}
		else
		{	
			//释放一个旧的cache节点
			nCacheIndex = 0;
			if(ms_ElemFileCache[0].uRefFlag)
				ms_ElemFileCache[0].uRefFlag--;

			for(int i = 1; i < MAX_XPACKFILE_CACHE; i++)
			{
				if(ms_ElemFileCache[i].uRefFlag)
					ms_ElemFileCache[i].uRefFlag--;

				if(ms_ElemFileCache[i].uRefFlag < 
					ms_ElemFileCache[nCacheIndex].uRefFlag)
					nCacheIndex = i;
			}

			FreeElemCache(nCacheIndex);
		}

		ms_ElemFileCache[nCacheIndex].pBuffer = pBuffer;
		ms_ElemFileCache[nCacheIndex].uId = m_pIndexList[nElemIndex].uId;
		ms_ElemFileCache[nCacheIndex].lSize = m_pIndexList[nElemIndex].lSize;
		ms_ElemFileCache[nCacheIndex].nPackIndex = m_nSelfIndex;
		ms_ElemFileCache[nCacheIndex].nElemIndex = nElemIndex;
		ms_ElemFileCache[nCacheIndex].uRefFlag = INFINITE_;

		return nCacheIndex;
	}

	/*
	 *	功能：读取子文件一定长度的数据到缓冲区
	 *	参数：	ElemRef --> 用来读取数据的参数
	 *			pBuffer --> 用来读取数据的缓冲区
	 *			uSize --> 要读取的数据的长度
	 *	返回：成功读取得字节数
	 */
	int XPackFile::ElemFileRead(
		PackElemFileRef& ElemRef, 
		void* pBuffer, 
		unsigned uSize)
	{
		if(ms_bInitialize == false)
			return 0;

		int nResult = 0;
		if(pBuffer && 
			ElemRef.uId &&
			ElemRef.nElemIndex >= 0)
		{
#ifdef WIN32
			::EnterCriticalSection(&ms_ReadCritical);
#endif

			//--先看是否已经在cache里了---
			ElemRef.nCacheIndex = FindElemFileInCache(ElemRef.uId, ElemRef.nCacheIndex);

			if(ElemRef.nCacheIndex < 0 &&								//cache里未找到
				ElemRef.nElemIndex < m_nElemFileCount &&
				m_pIndexList[ElemRef.nElemIndex].uId == ElemRef.uId)
			{
				void* pDataBuffer = ReadElemFile(ElemRef.nElemIndex);
				if(pDataBuffer)
					ElemRef.nCacheIndex = AddElemFileToCache(pDataBuffer, ElemRef.nElemIndex);
			}

			if(ElemRef.nCacheIndex >= 0 &&
				//此下面三项应该展开检查，防止被模块外部改变，引起错误。
				//为效率可考虑省略，但需外部按照规则随便改变ElemRef的内容。
				ElemRef.nPackIndex == ms_ElemFileCache[ElemRef.nCacheIndex].nPackIndex &&
				ElemRef.nElemIndex == ms_ElemFileCache[ElemRef.nCacheIndex].nElemIndex &&
				ElemRef.nSize == ms_ElemFileCache[ElemRef.nCacheIndex].lSize)
			{
				if(ElemRef.nOffset < 0)
					ElemRef.nOffset = 0;

				if(ElemRef.nOffset < ElemRef.nSize)
				{
					if(ElemRef.nOffset + (int)uSize <= ElemRef.nSize)
						nResult = uSize;
					else
						nResult = ElemRef.nSize - ElemRef.nOffset;

					::memcpy(
						pBuffer, 
						(char*)ms_ElemFileCache[ElemRef.nCacheIndex].pBuffer + ElemRef.nOffset,
						nResult);

					ElemRef.nOffset += nResult;
				}
				else
				{
					ElemRef.nOffset = ElemRef.nSize;
				}
			}

#ifdef WIN32
			::LeaveCriticalSection(&ms_ReadCritical);
#endif
		}
		return nResult;
	}

};
